#pragma once
#include <glew.h>
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>

using namespace std;

class Shader {
public:
	GLuint id;//shader program 的id
	Shader(const char* vPath, const char* fPath) {//输入的参数是vertex shader 的路径和fragment shader的路径，然后自动编译连接得到program
		string vTemp = this->readShader(vPath);
		string fTemp = this->readShader(fPath);
		const char* vCode= vTemp.c_str();
		const char* fCode= fTemp.c_str();

		GLuint vshader = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vshader, 1, &vCode, NULL);
		glCompileShader(vshader);
		checkShaderComile(vshader);

		GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fshader, 1, &fCode, NULL);
		glCompileShader(fshader);
		checkShaderComile(fshader);

		id = glCreateProgram();
		glAttachShader(id, vshader);
		glAttachShader(id, fshader);
		glLinkProgram(id);
		checkProgramLine(id);
		glDeleteShader(vshader);
		glDeleteShader(fshader);
	}

	void use() {//激活这个program
		glUseProgram(id);
	}

	void setVec3(const char* name, glm::vec3 value) {//修改vec3类型的uniform,参数是uniform的名字和需要设置成的值
		glUniform3fv(glGetUniformLocation(id, name), 1, glm::value_ptr(value));
	}

	void setMat4(const char* name, glm::mat4 value) {//修改Mat4类型的uniform,参数是uniform的名字和需要设置成的值
		glUniformMatrix4fv(glGetUniformLocation(id, name), 1, GL_FALSE, glm::value_ptr(value));
	}

private:
	string readShader(const char* filePath) {
		ifstream file(filePath, ios::in);
		if (!file.is_open()) {
			cerr << "error in read shader file:" << filePath << endl;
		}
		string line, result;
		while (getline(file, line))
			result += line + '\n';
		file.close();
		return result;
	}

	void printShaderLog(GLuint shader) {
		GLint len;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
		if (len > 0) {
			char* log = (char*)malloc(len);
			int nWritten;
			glGetShaderInfoLog(shader, len, &nWritten, log);
			cerr << "Shader Info Log:" << log << endl;
			free(log);
		}
	}
	void checkShaderComile(GLuint shader) {
		GLint success;
		glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
		if (!success) {
			printShaderLog(shader);
		}
	}

	void printProgramLog(GLuint program) {
		GLint len;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
		if (len > 0) {
			char* log = (char*)malloc(len);
			int nWritten;
			glGetShaderInfoLog(program, len, &nWritten, log);
			cerr << "Program Info Log:" << log << endl;
			free(log);
		}
	}
	void checkProgramLine(GLuint program) {
		GLint success;
		glGetProgramiv(program, GL_LINK_STATUS, &success);
		if (!success) {
			cerr << "Program Link Error:" << endl;
			printProgramLog(program);
		}
	}
};